package com.fox.stock.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fox.stock.pojo.vo.req.LoginReqVo;
import com.fox.stock.pojo.vo.resq.LoginRespVoExt;
import com.fox.stock.pojo.vo.resq.R;
import com.fox.stock.pojo.vo.resq.ResponseCode;
import com.fox.stock.security.util.JwtTokenUtil;
import com.fox.stock.security.vo.LoginUserDetail;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;


public class JwtLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private RedisTemplate redisTemplate;

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public JwtLoginAuthenticationFilter(String myUrl) {
        super(myUrl);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //判断请求方法必须是post提交，且提交的数据的内容必须是application/json格式的数据
        if (!request.getMethod().equals("POST") || !(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE) || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE))){
            throw new AuthenticationServiceException("Authentication method not supported:" + request.getMethod());
        }

        //获取请求参数
        //获取reqeust请求对象的发送过来的数据流
        ServletInputStream in = request.getInputStream();

        //将数据流中的数据反序列化成LoginREspVo对象
        LoginReqVo login = new ObjectMapper().readValue(in, LoginReqVo.class);
        // 判断数据是否合法
        if (login == null || StringUtils.isBlank(login.getUsername()) || StringUtils.isBlank(login.getPassword()) || StringUtils.isBlank(login.getSessionId()) || StringUtils.isBlank(login.getCode())){
            R<Object> error = R.error(ResponseCode.USERNAME_OR_PASSWORD_ERROR);
            response.getWriter().write(new ObjectMapper().writeValueAsString(error));
            return null;
        }

        // 封装前检查验证码是否合法
        // 判断验证码是否正确
        String code = (String) redisTemplate.opsForValue().get(login.getSessionId());
        if (StringUtils.isBlank(code) || !code.equalsIgnoreCase(login.getCode())) {
            response.getWriter().write(new ObjectMapper().writeValueAsString(R.error(ResponseCode.CHECK_CODE_ERROR)));
            return null;
        }

        // 验证码合法,封装数据
        String username = login.getUsername();
        username = (username != null) ? username : "";
        username = username.trim();
        String password = login.getPassword();
        password = (password != null) ? password : "";

        //将用户名和密码信息封装到认证票据对象下
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

        // Allow subclasses to set the "details" property
        //setDetails(request, authRequest);
        //调用认证管理器认证指定的票据对象
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest request,
                                            HttpServletResponse response,
                                            FilterChain chain,
                                            Authentication authResult) throws IOException, ServletException {
        //认证主体信息，此时以填充用户权限信息
        UserDetails userDetails = (UserDetails) authResult.getPrincipal();
        //组装响应前端的信息
        // 获取用户名称
        String username = userDetails.getUsername();
        // 获取权限集合对象
        Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
        String auStrList = authorities.toString();
        // 生成token字符串票据
        String token = JwtTokenUtil.createToken(username, auStrList);
        // 封装统一结果
        LoginRespVoExt resp = new LoginRespVoExt();
        BeanUtils.copyProperties(userDetails,resp);
        resp.setAccessToken(token);
        R<LoginRespVoExt> ok = R.ok(resp);
        //设置响应格式
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //内容编码格式
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(new ObjectMapper().writeValueAsString(ok));
    }

    /**
     * 认证失败处理方法
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request,
                                              HttpServletResponse response,
                                              AuthenticationException failed) throws IOException, ServletException {
        R<Object> error = R.error(ResponseCode.ERROR);
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write(new ObjectMapper().writeValueAsString(error));
    }
}
